package org.zjvis.datascience.web.controller;

import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson.JSONObject;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.zjvis.datascience.common.annotation.ProjectAuth;
import org.zjvis.datascience.common.dto.TaskDTO;
import org.zjvis.datascience.common.model.stat.ColumnStat;
import org.zjvis.datascience.common.util.TaskUtil;
import org.zjvis.datascience.common.widget.dto.WidgetDTO;
import org.zjvis.datascience.common.enums.ProjectAuthEnum;
import org.zjvis.datascience.common.widget.enums.WidgetTypeEnum;
import org.zjvis.datascience.common.exception.DataScienceException;
import org.zjvis.datascience.common.model.*;
import org.zjvis.datascience.common.util.JsonParseUtil;
import org.zjvis.datascience.common.util.RedisUtil;
import org.zjvis.datascience.common.util.TaskDTOUtil;
import org.zjvis.datascience.common.vo.column.ColumnQueryVO;
import org.zjvis.datascience.common.vo.dataset.DatasetQueryVO;
import org.zjvis.datascience.common.widget.vo.WidgetDataPageVO;
import org.zjvis.datascience.common.widget.vo.WidgetLoadGraphVO;
import org.zjvis.datascience.common.widget.vo.WidgetStatRequestVO;
import org.zjvis.datascience.service.TColumnService;
import org.zjvis.datascience.service.TaskInstanceService;
import org.zjvis.datascience.service.TaskService;
import org.zjvis.datascience.service.WidgetService;
import org.zjvis.datascience.service.dataprovider.GPDataProvider;
import org.zjvis.datascience.service.dataset.DatasetService;
import org.zjvis.datascience.service.graph.GraphFileService;

import javax.servlet.http.HttpServletRequest;
import javax.validation.Valid;
import java.util.List;

import static org.zjvis.datascience.common.constant.IdConstant.*;
import static org.zjvis.datascience.common.widget.WidgetJsonConstant.*;

/**
 * @description 可视化渲染接口 Controller
 * @date 2021-12-29
 */
@Api(tags = "widget", description = "可视化渲染接口")
@RestController
@RequestMapping("/widget/render")
public class WidgetRenderController {

    private final static Logger logger = LoggerFactory.getLogger(WidgetRenderController.class);

    private final static String HTTP_UPLOAD = "http";

    private final static String DEFAULT_SORT = "desc";

    @Autowired
    private WidgetService widgetService;

    @Autowired
    private GPDataProvider gpDataProvider;

    @Autowired
    private DatasetService datasetService;

    @Autowired
    private TaskService taskService;

    @Autowired
    private TColumnService tColumnService;

    @Autowired
    private TaskInstanceService taskInstanceService;

    @Autowired
    private GraphFileService graphFileService;

    @Autowired
    public RedisUtil redisUtil;

    //TODO 查看dashboard 以及 widget 各类json的 存储类型 widget -> MEDIUMTEXT 16MB  dashboard -> LONGTEXT 4GB
    @PostMapping(value = "/loadGraphData")
    @ResponseBody
    @ApiOperation(value = "加载图数据文件", notes = "加载图数据文件")
    public ApiResult<JSONObject> loadGraphData(@RequestBody @ProjectAuth @Valid WidgetLoadGraphVO vo) {
        try {
            return ApiResult.valueOf(graphFileService.graphFile2Json(vo.getFileName()));
        } catch (DataScienceException e1) {
            logger.error("load graph data failed, since {}", e1.getMessage());
            return ApiResult.valueOf(ApiResultCode.DATASET_INFO_NULL);
        } catch (Exception e2) {
            logger.error("load graph data failed, since {}", e2.getMessage());
            return ApiResult.valueOf(ApiResultCode.DATA_NULL);
        }
    }

    @PostMapping(value = "/queryStat")
    @ResponseBody
    @ApiOperation(value = "可视化视图右侧边栏获取详细数据")
    public ApiResult<ColumnStat> queryStat(HttpServletRequest r, @RequestBody @Valid WidgetStatRequestVO vo) {
        if (vo.isFromTask()){
            vo.setTableName(TaskUtil.extractTableStr(taskService.queryById(vo.getDataId())));
        }
        ColumnStat columnStat = tColumnService.querySimpleStat(vo.toColumnQueryVO(), true, false);
        if (ObjectUtil.isNotNull(columnStat)) {
            columnStat.setTable("");
            columnStat.setTopColumn(null);
        }
        return ApiResult.valueOf(columnStat);
    }

    @PostMapping(value = "/queryColumn")
    @ResponseBody
    @ApiOperation(value = "可视化视图右侧边栏获取详细数据")
    public ApiResult<Object> queryColumn(HttpServletRequest request,
                                           @ProjectAuth(auth = ProjectAuthEnum.READ) @RequestBody @Valid WidgetDataPageVO vo) {
        JSONObject result = null;
        if (vo.getTypeEnum().isDataNode()) {

            String serverName = request.getServerName();
            result = datasetService.queryDataById(vo.getId(), vo.getCurPage(), vo.getPageSize());
            JSONObject data = result.getJSONObject("data");
            //http数据导入的url需要添加当前域名
            if (HTTP_UPLOAD.equals(data.getString("type"))) {
                data.put("dataConfig", serverName + data.getString("dataConfig"));
                result.put("data", data);
            }
            if (result.getInteger("code") != 200) {
                return ApiResult.valueOf(result.getInteger("code"), result.getString("errMsg"), "");
            } else {
                return ApiResult.valueOf(result.get("data"));
            }

        } else if (vo.getTypeEnum().isTaskNode()) {
            JSONObject ret = null;
            if (vo.getTypeEnum().equals(WidgetTypeEnum.TCLEAN)) {
                ret = taskInstanceService.queryDataById(vo.getId(), vo.getCurPage(), vo.getPageSize());
            } else {
                ColumnQueryVO columnQueryVO = ColumnQueryVO.init();
                columnQueryVO.setTaskId(vo.getId());
                columnQueryVO.setCurPage(vo.getCurPage());
                columnQueryVO.setPageSize(vo.getPageSize());
                columnQueryVO.setTable(widgetService.getAssociateTableName(WidgetTypeEnum.TASK, vo.getId()));
                ret = tColumnService.queryDetail(columnQueryVO);
            }
            return ApiResult.valueOf(ret);
        } else {
            return ApiResult.valueOf("获取数据列信息失败");
        }
    }

    @PostMapping(value = "/getData")
    @ResponseBody
    @ApiOperation(value = "可视化数据", notes = "根据配置获取可视化处理数据")
    public ApiResult<AggregateResult> getData(HttpServletRequest r, @RequestBody JSONObject jsonObj) {
        AggregateResult ret = null;
        //获取图表widget类型
        WidgetTypeEnum typeEnum = WidgetTypeEnum.valueOf(StringUtils.upperCase(jsonObj.getString("type")));
        //widgetJson用于区分是系统widget 还是 数据视图带过来的widget (系统widget没有)
        JSONObject widgetJson = jsonObj.getJSONObject(WIDGET_JSON);

        String tableName = widgetService.getAssociateTableName(typeEnum, jsonObj.getLong(ID));
        if (StringUtils.isEmpty(tableName)) {
            return ApiResult.valueOf(ApiResultCode.DATA_NULL, null, "系统找不到对应的数据表！");
        }

        try {
            if (null != widgetJson) {
                //task dataset 的 聚合查询
                if (widgetJson.getBooleanValue(IS_AGGR_QUERY)) {
                    AggrConfig config = AggrConfig.parse(null, widgetJson);
                    Table table = new Table(1L, tableName);
                    ret = gpDataProvider.getData(table, config);
                } else {
                    ret = parseNnAggrQuery(typeEnum, jsonObj, tableName);
                }
            } else if (jsonObj.containsKey(TABLE_JSON)) {
                JSONObject tableJson = jsonObj.getJSONObject(TABLE_JSON);
                DatasetQueryVO queryVO = DatasetQueryVO.parse(tableJson); //queryVO 里面的sortCol 是为了 数据表格的升序降序
                List<ConfigComponent> filters = AggrConfig.parseFilter(tableJson.getJSONArray(FILTERS));
                ret = gpDataProvider.getDataDetail(tableName, null, queryVO.getPageSize(), new JSONObject(), filters, queryVO);
                ret.setPageInfo(queryVO);
            } else {
                // TEXT文本控件 不需要查数据，直接拿dataJson
                if (typeEnum.equals(WidgetTypeEnum.TEXT)) {
                    WidgetDTO textWidget = widgetService.queryById(jsonObj.getLong(ID));
                    ret = new AggregateResult(textWidget.getDataJson());
                }
            }
        }catch(DataScienceException e){
            return ApiResult.valueOf(ApiResultCode.INVALID_DATA_CONDITION, null, e.getCause().getMessage());
        }
        //地理经纬度 再处理 (何种类型的widget都有可能 存在地理数据)
        if (null != ret && widgetService.needConvert(jsonObj)) {
            widgetService.convertCoordinate(ret, jsonObj);
        }
        if (StringUtils.isNotEmpty(ret.getErrorMsg())){
            return ApiResult.error(ApiResultCode.INVALID_DATA_CONDITION, ret.getErrorMsg());
        }
        return ApiResult.valueOf(ret);
    }

    /**
     * 解析 非聚合查询类的 图表widget 渲染
     *
     * @param typeEnum
     * @param jsonObj
     * @param tableName
     * @return
     */
    private AggregateResult parseNnAggrQuery(WidgetTypeEnum typeEnum, JSONObject jsonObj, String tableName) {
        AggregateResult ret = null;
        JSONObject config = JsonParseUtil.getValueByKey("widgetJson.config", jsonObj, JSONObject.class);
        JSONObject categoryOrder = null;
        if (typeEnum.equals(WidgetTypeEnum.RECOMMEND)) {
            try {
                TaskDTO taskDTO = taskService.queryById(jsonObj.getLong(TASK_ID));
                categoryOrder = TaskDTOUtil.getValueByKey("output[0].categoryOrder", taskDTO, JSONObject.class);
            } catch (Exception e) {
                logger.error("error happened when rendering widget {} 's data, since {}", jsonObj.getLong(ID), e.getMessage());
            }
        }
        JSONObject sortJson = wrapperSortJson(config, categoryOrder);
        List<ConfigComponent> filters = AggrConfig.parseFilter(config.getJSONArray(FILTERS));
        if (typeEnum.equals(WidgetTypeEnum.RECOMMEND) &&
                CLUSTER_ID.equals(JsonParseUtil.getValueByKey("keys[0].col", config, String.class))) {
            ret = gpDataProvider.getDataDetailTemp(tableName, null, config.getInteger(TOP_N), sortJson, filters, jsonObj);
        } else {
            ret = gpDataProvider.getDataDetail(tableName, null, config.getInteger(TOP_N), sortJson, filters, null);
        }
        return ret;
    }

    /**
     * 推荐出来的图表的SortJson解析
     * {"config":{"topN":50,"filters":[],
     * "keys":[{"col":"id_after","values":"","filterType":"","sort":"asc"}],
     * "values":[{"col":"count_customer_id","sort":"","topN":50}]}}
     *
     * @param config
     * @param categoryOrder
     * @return
     */
    private JSONObject wrapperSortJson(JSONObject config, JSONObject categoryOrder) {
        JSONObject sortJson = new JSONObject();
        List<JSONObject> sortArr = config.getJSONArray(KEYS).toJavaList(JSONObject.class);
        sortArr.addAll(config.getJSONArray(VALUES).toJavaList(JSONObject.class));

        for (JSONObject obj : sortArr) {
            String col = obj.getString(COL);
            if (StringUtils.isNotEmpty(obj.getString(SORT))) {
                String sort = DEFAULT_SORT;
                if (null != categoryOrder && categoryOrder.containsKey(col)) {
                    sort = categoryOrder.getString(col);
                }else {
                    sort = obj.getString(SORT);
                }
                sortJson.put(col, sort);
            }
        }

        return sortJson;
    }

}
